package cn.xieyang.gen.xcode;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;


@Slf4j
public class XCodeGenerator {

    public static final String CUSTOMER_AREA_START_MARK = "// ==============================customer area start==============================";
    public static final String CUSTOMER_AREA_END_MARK = "// ==============================customer area end==============================";

    public static final String IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK = "// ==============================import and annotation and classDefine customer area start==============================";
    public static final String IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK = "// ==============================import and annotation and classDefine customer area end==============================";

    public static final String XML_CUSTOMER_AREA_START_MARK = "<!--customer area start-->";
    public static final String XML_CUSTOMER_AREA_END_MARK = "<!--customer area end-->";

    public static ThreadLocal<String> projectDirLocal = new ThreadLocal<>();


    /**
     * 脚本:删除生成的文件，然后重新生成。在生成出来的文件编译，导致无法重新生成的时候使用。
     * rm -rf D:/code/x-all/gme-hmty-app/gme-hmty-infra/src/main/java/com/x/infra/data/*
     * rm -rf D:/code/x-all/gme-hmty-app/gme-hmty-domain/src/main/java/*
     * rm -rf D:/code/x-all/gme-hmty-app/gme-hmty-domain/src/main/java/com/x/domain/bo/*
     * rm -rf D:/code/x-all/gme-hmty-app/gme-hmty-dto/src/main/java/com/x/dto/*
     */
    public static void main(String[] args) {
        projectDirLocal.set("D:/code/x-all/gme-hmty-app");
        gen("exercise_tbl,exercise_dtl_tbl");
    }

    public static void gen(String include) {
        HashSet<String> filePathSet = new HashSet<>();
        FastAutoGenerator fastAutoGenerator = FastAutoGenerator
                .create("jdbc:mysql://192.168.196.3:3306/ganjiang-db", // 数据库连接 URL
                        "root", // 数据库用户名
                        "123456") // 数据库密码
                .globalConfig(builder -> {
                    builder.author("xsy") // 设置作者
                            .enableSwagger()        // 开启 swagger 模式 默认值:false
                            .disableOpenDir()       // 禁止打开输出目录 默认值:true
                            // 关闭注释日期，防止没有改动也修改日期
//                            .commentDate("yyyy-MM-dd") // 注释日期
                            .dateType(DateType.ONLY_DATE);   //定义生成的实体类中日期类型 DateType.ONLY_DATE 默认值: DateType.TIME_PACK
                }).packageConfig(builder -> {
                    Map<OutputFile, String> pathInfoMap = new HashMap<>();
                    pathInfoMap.put(OutputFile.entity, projectDirLocal.get() + "/gme-hmty-infra/src/main/java/com/x/infra/data");
                    pathInfoMap.put(OutputFile.xml, projectDirLocal.get() + "/gme-hmty-infra/src/main/resources/mapper");
                    pathInfoMap.put(OutputFile.mapper, projectDirLocal.get() + "/gme-hmty-infra/src/main/java/com/x/infra/mapper");
                    pathInfoMap.put(OutputFile.service, projectDirLocal.get() + "/gme-hmty-infra/src/main/java/com/x/infra/myservice");
                    pathInfoMap.put(OutputFile.serviceImpl, projectDirLocal.get() + "/gme-hmty-infra/src/main/java/com/x/infra/service/impl");
                    pathInfoMap.put(OutputFile.controller, projectDirLocal.get() + "/gme-hmty-adapter/src/main/java/com/x/adapter/controller");
                    builder.parent("com") // 设置父包名
                            .moduleName("x") // 设置模块名（如：model, mapper, service）
                            .entity("infra.data") // 实体类所在子包
                            .mapper("infra.mapper") // Mapper 接口所在子包
                            .service("infra.myservice") // Service 类所在子包
                            .serviceImpl("infra.service.impl") // Service 类所在子包
                            .controller("adapter.controller") //controller类所在子包
                            .pathInfo(pathInfoMap) // 设置文件生成路径，xml文件默认存放在mapper的xml下
                    ;
                }).injectionConfig(consumer -> {
                    List<BiConsumer<TableInfo, Map<String, Object>>> oprlist = new ArrayList<>();
                    List<CustomFile> customFileList = new ArrayList<>();
                    HashMap<String, Object> customMap = new HashMap<>();
                    // mp底层配置
                    // 关闭lombok注解,生成setter和getter方法
                    customMap.put("entityLombokModel", false);
                    // 开启 chainModel ,set 返回this
                    customMap.put("chainModel", true);
                    // abs
                    injectAbsConfig(customFileList, customMap, oprlist);
                    // data
                    injectDataConfig(customFileList, customMap, oprlist, filePathSet);
                    // bo
                    injectBOConfig(customFileList, customMap, oprlist, filePathSet);
                    // do
                    injectDOConfig(customFileList, customMap, oprlist, filePathSet);
                    // dto
                    injectDTOConfig(customFileList, customMap, oprlist, filePathSet);
                    // xml
                    injectXmlConfig(customFileList, customMap, oprlist, filePathSet);
                    // mapper
                    injectMapperConfig(customFileList, customMap, oprlist, filePathSet);
                    // service
                    injectServiceConfig(customFileList, customMap, oprlist, filePathSet);
                    // serviceImpl
                    injectServiceImplConfig(customFileList, customMap, oprlist, filePathSet);
                    // api
                    injectApiConfig(customFileList, customMap, oprlist, filePathSet);
                    // controller
                    injectControllerConfig(customFileList, customMap, oprlist, filePathSet);
                    // objectMap，所有的模版变量都在
                    consumer.beforeOutputFile((tableInfo, objectMap) -> {
                        oprlist.forEach(curOpr -> curOpr.accept(tableInfo, objectMap));
                    });
                    consumer.customFile(customFileList);
                    consumer.customMap(customMap);
                }).strategyConfig(builder -> {
                    builder.addInclude(include) // 多张表，用逗号","分隔
                            .addTablePrefix("") // 表前缀（如果有） .addTablePrefix("tb_", "gms_") // 设置过滤表前缀
                            .addTableSuffix("_tbl") // 表后缀（如果有），生成${entity}的时候，会删除掉后缀
                            // entity
                            .entityBuilder() // 实体策略配置
                            .enableLombok()
                            .enableFileOverride()
                            .disable() // 禁止生产entity文件
                            // mapper
                            .mapperBuilder()// mapper策略配置
                            .formatMapperFileName("%sMapper").enableMapperAnnotation()   //@mapper注解开启,@Deprecated
                            .formatXmlFileName("%sMapper").enableFileOverride()
                            // controller
                            .controllerBuilder() // controller 策略配置
                            .formatFileName("%sController").enableRestStyle() // 开启RestController注解
                            .enableFileOverride()
                            // service
                            .serviceBuilder()// service策略配置
                            .formatServiceFileName("%sService").formatServiceImplFileName("%sServiceImpl").enableFileOverride();
                })
                // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .templateEngine(new FreemarkerTemplateEngine());
        // 执行代码生成
        try {
            fastAutoGenerator.execute();
            log.info("生成成功!");
        } catch (Exception e) {
            filePathSet.stream().forEach(filePath -> {
                if (FileUtil.exist(filePath)) {
                    FileUtil.del(filePath);
                }
            });
            log.error("生成失败!", e);
        }
    }


    private static void injectMapperConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        // mapper模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.infra.mapper");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> mapperOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String fileName = "";
            ConfigBuilder configBuilder = (ConfigBuilder) objectMap.get("config");
            Map<OutputFile, String> pathInfo = configBuilder.getPathInfo();
            String filePath = pathInfo.get(OutputFile.mapper);
            if (Objects.nonNull(filePath) && filePath.endsWith(File.separator)) {
                fileName = filePath + entityName + "Mapper.java";
            } else {
                fileName = filePath + File.separator + entityName + "Mapper.java";
            }
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        oprlist.add(mapperOpr);
        customMap.put("mapperVars", curVars);
    }

    private static void injectXmlConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        // mapper模版变量
        HashMap<String, String> xmlVars = new HashMap<>();
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> xmlOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String fileName = "";
            ConfigBuilder configBuilder = (ConfigBuilder) objectMap.get("config");
            Map<OutputFile, String> pathInfo = configBuilder.getPathInfo();
            String filePath = pathInfo.get(OutputFile.xml);
            if (Objects.nonNull(filePath) && filePath.endsWith(File.separator)) {
                fileName = filePath + entityName + "Mapper.xml";
            } else {
                fileName = filePath + File.separator + entityName + "Mapper.xml";
            }
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            xmlVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, XML_CUSTOMER_AREA_START_MARK, XML_CUSTOMER_AREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        oprlist.add(xmlOpr);
        customMap.put("xmlVars", xmlVars);
    }

    private static void injectServiceConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        // mapper模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.infra.myservice");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> serviceOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String fileName = "";
            ConfigBuilder configBuilder = (ConfigBuilder) objectMap.get("config");
            Map<OutputFile, String> pathInfo = configBuilder.getPathInfo();
            String filePath = pathInfo.get(OutputFile.service);
            if (Objects.nonNull(filePath) && filePath.endsWith(File.separator)) {
                fileName = filePath + entityName + "Service.java";
            } else {
                fileName = filePath + File.separator + entityName + "Service.java";
            }
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        oprlist.add(serviceOpr);
        customMap.put("serviceVars", curVars);
    }

    private static void injectServiceImplConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        // mapper模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.infra.service.impl");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> serviceImplOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String fileName = "";
            ConfigBuilder configBuilder = (ConfigBuilder) objectMap.get("config");
            Map<OutputFile, String> pathInfo = configBuilder.getPathInfo();
            String filePath = pathInfo.get(OutputFile.serviceImpl);
            if (Objects.nonNull(filePath) && filePath.endsWith(File.separator)) {
                fileName = filePath + entityName + "ServiceImpl.java";
            } else {
                fileName = filePath + File.separator + entityName + "ServiceImpl.java";
            }
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        oprlist.add(serviceImplOpr);
        customMap.put("serviceImplVars", curVars);
    }

    private static void injectDataConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        CustomFile file = new CustomFile.Builder()
                .fileName(".java")
                .templatePath("/templates/x-demo-app/data.java.ftl")
                .filePath(projectDirLocal.get() + "/gme-hmty-common/src/main/java/")
                .packageName("com.xieyang.common.data")
                .enableFileOverride().build();
        // data 模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.common.data");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> curOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String filePath = file.getFilePath();
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        // 添加模板文件配置。若不添加，则不生成文件。
        customFileList.add(file);
        customMap.put("dataVars", curVars);
        oprlist.add(curOpr);
    }


    private static void injectBOConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        CustomFile file = new CustomFile.Builder()
                .fileName("BO.java")
                .templatePath("/templates/x-demo-app/BO.java.ftl")
                .filePath(projectDirLocal.get() + "/gme-hmty-domain/src/main/java/")
                .packageName("com.xieyang.domain.bo")
                .enableFileOverride().build();
        // bo 模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.domain.bo");
        // data 包名
        curVars.put("dataPackageName", "com.xieyang.common.data");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> curOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String filePath = file.getFilePath();
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        // 添加模板文件配置。若不添加，则不生成文件。
        customFileList.add(file);
        customMap.put("boVars", curVars);
        oprlist.add(curOpr);
    }

    private static void injectDOConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        CustomFile file = new CustomFile.Builder()
                .fileName("DO.java")
                .templatePath("/templates/x-demo-app/DO.java.ftl")
                .filePath(projectDirLocal.get() + "/gme-hmty-infra/src/main/java/")
                .packageName("com.xieyang.infra.data")
                .enableFileOverride().build();
        // do 模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.infra.data");
        // data 包名
        curVars.put("dataPackageName", "com.xieyang.common.data");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> curOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String filePath = file.getFilePath();
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        // 添加模板文件配置。若不添加，则不生成文件。
        customFileList.add(file);
        customMap.put("doVars", curVars);
        oprlist.add(curOpr);
    }

    private static void injectDTOConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        CustomFile file = new CustomFile.Builder()
                .fileName("DTO.java")
                .templatePath("/templates/x-demo-app/DTO.java.ftl")
                .filePath(projectDirLocal.get() + "/gme-hmty-dto/src/main/java/")
                .packageName("com.xieyang.dto").enableFileOverride().build();
        // api模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.dto");
        // data 包名
        curVars.put("dataPackageName", "com.xieyang.common.data");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> curOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String filePath = file.getFilePath();
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        // 添加模板文件配置。若不添加，则不生成文件。
        customFileList.add(file);
        customMap.put("dtoVars", curVars);
        oprlist.add(curOpr);
    }

    private static void injectAbsConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist) {
        // 抽象层模版变量
        HashMap<String, String> absVars = new HashMap<>();
        absVars.put("customerAreaStartMark", CUSTOMER_AREA_START_MARK);
        absVars.put("customerAreaEndMark", CUSTOMER_AREA_END_MARK);
        absVars.put("xmlCustomerAreaStartMark", XML_CUSTOMER_AREA_START_MARK);
        absVars.put("xmlCustomerAreaEndMark", XML_CUSTOMER_AREA_END_MARK);
        absVars.put("importAnnotationClassDefineAreaStartMark", IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK);
        absVars.put("importAnnotationClassDefineAreaEndMark", IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK);
        customMap.put("abs", absVars);
    }

    /**
     * 生成api.java文件的配置
     *
     * @param customFileList 自定义模板文件配置
     * @param customMap      自定义配置 Map 对象
     * @param oprlist        根据tableInfo和objectMap，生成模版变量，并放入模版变量Map中的操作
     * @param filePathSet
     */
    private static void injectApiConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        CustomFile file = new CustomFile.Builder()
                // 生成文件的后缀，${entity}Api.java
                .fileName("Api.java")
                // 模版文件路径，相对路径
                .templatePath("/templates/x-demo-app/api.java.ftl")
                // 顶级包名的父目录位置，绝对路径
                .filePath(projectDirLocal.get() + "/gme-hmty-api/src/main/java")
                // 包名
                .packageName("com.xieyang.api")
                // 文件名称格式化，根据表信息组装文件名。组装规则：entityName + fileName
                .formatNameFunction(TableInfo::getEntityName)
                // 文件存在，则覆盖
                .enableFileOverride().build();
        // api模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.api");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> apiOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String filePath = file.getFilePath();
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        // 添加模板文件配置。若不添加，则不生成文件。
        customFileList.add(file);
        customMap.put("apiVars", curVars);
        oprlist.add(apiOpr);
    }

    /**
     * 生成 controller.java文件的配置
     *
     * @param customFileList 自定义模板文件配置
     * @param customMap      自定义配置 Map 对象
     * @param oprlist        根据tableInfo和objectMap，生成模版变量，并放入模版变量Map中的操作
     * @param filePathSet
     */
    private static void injectControllerConfig(List<CustomFile> customFileList, HashMap<String, Object> customMap, List<BiConsumer<TableInfo, Map<String, Object>>> oprlist, HashSet<String> filePathSet) {
        CustomFile file = new CustomFile.Builder()
                // 生成文件的后缀，${entity}Controller.java
                .fileName("Controller.java")
                // 模版文件路径，相对路径
                .templatePath("/templates/controller.java.ftl")
                // 顶级包名的父目录位置，绝对路径
                .filePath(projectDirLocal.get() + "/gme-hmty-adapter/src/main/java")
                // 包名
                .packageName("com.xieyang.adapter.controller")
                // 文件名称格式化，根据表信息组装文件名。组装规则：entityName + fileName
                .formatNameFunction(TableInfo::getEntityName)
                // 文件存在，则覆盖
                .enableFileOverride().build();
        // api模版变量
        HashMap<String, String> curVars = new HashMap<>();
        // 包名
        curVars.put("packageName", "com.xieyang.adapter.controller");
        // 根据tableInfo和objectMap，生成模版变量，并放入模版变量Map
        BiConsumer<TableInfo, Map<String, Object>> apiOpr = (tableInfo, objectMap) -> {
            // 表entity名。若配置去掉tbl后缀，则entityName无entity后缀。
            String entityName = tableInfo.getEntityName();
            //<editor-fold desc="根据com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine.outputCustomFile方法，提取部分代码，并修改，获取输出文件绝对路径">
            String filePath = file.getFilePath();
            if (StringUtils.isNotBlank(file.getPackageName())) {
                filePath = filePath + File.separator + file.getPackageName().replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
            }
            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();
            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();
            //</editor-fold>
            // 获取原代码中的自定义代码部分，用于放到新代码中
            curVars.put("customerArea", getMiddleLinesStrByLineMark(fileName, CUSTOMER_AREA_START_MARK, CUSTOMER_AREA_END_MARK));
            // 获取import、annotation和classDefine部分
            curVars.put("importAnnotationClassDefineArea", getMiddleLinesStrByLineMark(fileName, IMPORT_ANNOTATION_CLASSDEFINEAREA_START_MARK, IMPORT_ANNOTATION_CLASSDEFINEAREA_END_MARK));
            // 文件路径添加到集合中
            filePathSet.add(fileName);
        };
        // 添加模板文件配置。若不添加，则不生成文件。
        customFileList.add(file);
        customMap.put("controllerVars", curVars);
        oprlist.add(apiOpr);
    }


    public static String getMiddleLinesStrByLineMark(String path, String startMarkLine, String endMarkLine) {
        return getMiddleLinesStrByLineMark(path, startMarkLine, endMarkLine, true);
    }

    /**
     * 获取startMarkLine和endMarkLine的中间行，不包括startMarkLine和endMarkLine。
     *
     * @param path          文件绝对路径
     * @param startMarkLine 开始行标志
     * @param endMarkLine   结束行标志
     * @param isTrimLine    是否去除行首和行尾的空格
     * @return 返回startMarkLine和endMarkLine的中间行
     */
    public static String getMiddleLinesStrByLineMark(String path, String startMarkLine, String endMarkLine, Boolean isTrimLine) {
        if (!FileUtil.exist(path)) return "";
        if (StringUtils.isAnyEmpty(startMarkLine, endMarkLine)) {
            throw new RuntimeException("startMarkLine和endMarkLine都不能为空");
        }
        StringBuilder rs = new StringBuilder();
        boolean isStartRecord = false;
        List<String> lines = FileUtil.readLines(path, StandardCharsets.UTF_8);

        for (int i = 0; i < lines.size(); i++) {
            String curLine = lines.get(i);
            String nextLine = ((i + 1) <= lines.size() - 1) ? lines.get(i + 1) : "";
            nextLine = Boolean.TRUE.equals(isTrimLine) ? nextLine.trim() : nextLine;
            boolean isEndRecordLine = nextLine.equals(endMarkLine);
            String markLine = Boolean.TRUE.equals(isTrimLine) ? curLine.trim() : curLine;
            if (isStartRecord) {
                rs.append(curLine);
                // 最后一行不添加空行
                if (!isEndRecordLine) rs.append("\n");
            }
            // 从下一行，开始记录
            if (startMarkLine.equals(markLine)) {
                isStartRecord = true;
            }
            // 从当前行，停止记录，并跳出循环
            if (isEndRecordLine) break;
        }
        return rs.toString();
    }

}
